﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

using SharedGenomics.Core;
using SharedGenomics.Annotations;

namespace SharedGenomics.Workbench.Annotations
{
    /// <summary>
    /// Represents a set of annotation requests related to a SNP - it is initialised with a set of annotations to fetch and is updated as these become available.
    /// </summary>
    public class AnnotationInfoSet
    {
        private IDictionary<AnnotationKey, AnnotationInfo> _annotationTypeMap;
        private int _finishedCount;
        private int _failedCount;
        private AnnotationValue _annotationRoot;

        /// <summary>
        /// Creates a new instance of this class - the set of annotations to fetch cannot be updated once the object has been created.
        /// </summary>
        /// <param name="root">The root of the annotation heirarchy - this will usually be a SNP name</param>
        /// <param name="descriptors">A collection of <see cref="AnnotationDescriptor"/> objects corresponding to the annotations to fetch for this set</param>
        public AnnotationInfoSet(AnnotationValue root, IEnumerable<AnnotationDescriptor> descriptors)
        {
            root.ThrowIfNull("root");
            this._annotationRoot = root;

            HashSet<AnnotationDescriptor> descriptorSet = new HashSet<AnnotationDescriptor>((descriptors ?? Enumerable.Empty<AnnotationDescriptor>()).Where(ad => ad != null));
            this._annotationTypeMap = descriptorSet.ToDictionary(ad => ad.Key, ad => new AnnotationInfo(ad));
        }

        public void AddAnnotation(IAnnotation annotation)
        {
            annotation.ThrowIfNull("annotation");

            AnnotationKey key = annotation.AnnotationKey;
            if (this._annotationTypeMap.ContainsKey(key))
            {
                this._annotationTypeMap[key].Finish(annotation);
                Interlocked.Increment(ref this._finishedCount);
            }
        }

        public void AnnotationFailed(AnnotationKey annotationKey)
        {
            if (annotationKey != null && this._annotationTypeMap.ContainsKey(annotationKey))
            {
                this._annotationTypeMap[annotationKey].Fail();
                Interlocked.Increment(ref this._failedCount);
            }
        }

        public AnnotationInfo this[AnnotationKey key]
        {
            get
            {
                return key == null
                    ? null
                    : this._annotationTypeMap.GetValueWithDefault(key);
            }
        }

        public int Count
        {
            get { return this._annotationTypeMap.Count; }
        }

        public int FinishedCount
        {
            get { return this._finishedCount; }
        }

        public int FailedCount
        {
            get { return this._failedCount; }
        }

        public object AnnotationTarget
        {
            get { return this._annotationRoot.Value; }
        }

        public IEnumerable<AnnotationInfo> Annotations
        {
            //NOTE: this is thread-safe since the collection of values is fixed for the life of this set
            get { return this._annotationTypeMap.Values.AsEnumerable(); }
        }
    }
}
